package com.song.uaa.gateway.service;

import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.stereotype.Service;

import com.song.uaa.gateway.domain.Authority;
import com.song.uaa.gateway.domain.User;
import com.song.uaa.gateway.service.dto.AdminUserDTO;

import reactor.core.publisher.Mono;

import static org.springframework.security.oauth2.core.oidc.StandardClaimNames.*;

@Service
public class UserService {
	
	/**
	 * 从令牌中获取用户信息
	 * @param authToken 令牌
	 * @return 用户
	 */
	public Mono<AdminUserDTO> getUserFromAuthentication(AbstractAuthenticationToken authToken) {
		Map<String, Object> attributes;
		if (authToken instanceof OAuth2AuthenticationToken) {
			// 通过OAuth2,
			OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken)authToken;
			attributes = oauthToken.getPrincipal().getAttributes();
		} else if(authToken instanceof JwtAuthenticationToken) {
			// 通过JWT 令牌
			JwtAuthenticationToken jwtToken = (JwtAuthenticationToken) authToken;
			attributes = jwtToken.getTokenAttributes();
		} else {
			throw new IllegalArgumentException("AuthenticationToken 不是 Oauth2 token 或 JWT token");
		}
		User user = getUser(attributes);
		user.setAuthorities(
				authToken
					.getAuthorities()
					.stream()
					.map(GrantedAuthority::getAuthority)
					.map(authority -> {
						Authority auth = new Authority();
						auth.setName(authority);
						return auth;
					})
					.collect(Collectors.toSet())
				);
		return syncUserWithIdp(attributes, user).flatMap(u -> Mono.just(new AdminUserDTO(u)));
	}
	
	/**
	 * 同步令牌中用户信息到本地库中
	 * @param attributes 令牌属性
	 * @param user 用户
	 * @return 用户
	 */
	private Mono<User> syncUserWithIdp(Map<String, Object> attributes, User user) {
		//TODO: 将认证信息同步到本地
		return Mono.just(user);
	}

	/**
	 * 从令牌属性中创建用户
	 * @param attributes 令牌属性
	 * @return 用户
	 */
	private User getUser(Map<String, Object> attributes) {
		User user = new User();
		String sub = String.valueOf(attributes.get(SUB));
		if(attributes.get("uid") != null) {
			user.setId((String)attributes.get("uid"));
		} else {
			user.setId(sub);
		}
		user.setLogin(sub);
		if(attributes.get(NAME) != null) {
			user.setName((String)attributes.get(NAME));
		}
		return user;
	}
}
